package cn.sxgan.core.services.impl;

import cn.sxgan.common.entity.vo.SystemInfoVO;
import cn.sxgan.common.response.Result;
import cn.sxgan.core.services.SystemService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.File;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @Description: 系统基本信息监控服务
 * @Author: sxgan
 * @Date: 2025-03-18 14:18
 * @Version: 1.0
 **/
@Slf4j
@Service
public class SystemServiceImpl implements SystemService {
    
    public Result<SystemInfoVO> findSystemBaseInfo() {
        var systemInfo = getSystemInfo();
        var cpuInfo = getCpuInfo();
        var memoryInfo = getMemoryInfo();
        var diskInfoList = getDiskInfoList();
        var jvmInfo = getJvmInfo();
        var networkInfo = getNetworkInfo();
        SystemInfoVO systemInfoVO = new SystemInfoVO(systemInfo, cpuInfo, memoryInfo, diskInfoList, jvmInfo, networkInfo);
        return Result.success(systemInfoVO);
    }
    
    private SystemInfoVO.SystemInfo getSystemInfo() {
        OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
        String computerName = Optional.ofNullable(System.getenv("COMPUTERNAME"))
                .orElse(System.getenv("HOSTNAME"));
        String userName = System.getProperty("user.name");
        String kernelVersion = System.getProperty("os.version");
        String uptime = getSystemUptime();
        
        return new SystemInfoVO.SystemInfo(
                System.getProperty("os.name"),
                System.getProperty("os.version"),
                System.getProperty("os.arch"),
                computerName,
                userName,
                kernelVersion,
                uptime
        );
    }
    
    public String getSystemUptime() {
        OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
        long uptime = System.currentTimeMillis() - ManagementFactory.getRuntimeMXBean().getStartTime();
        return formatUptime(uptime);
    }
    
    private String formatUptime(long uptimeInSeconds) {
        long days = uptimeInSeconds / 86400;
        long hours = (uptimeInSeconds % 86400) / 3600;
        long minutes = (uptimeInSeconds % 3600) / 60;
        long seconds = uptimeInSeconds % 60;
        
        return String.format("%d days, %d hours, %d minutes, %d seconds", days, hours, minutes, seconds);
    }
    
    private SystemInfoVO.CpuInfo getCpuInfo() {
        var osBean = ManagementFactory.getOperatingSystemMXBean();
        return new SystemInfoVO.CpuInfo(
                osBean.getSystemLoadAverage() * 100,
                osBean.getSystemLoadAverage()
        );
    }
    
    private SystemInfoVO.MemoryInfo getMemoryInfo() {
        var memoryBean = ManagementFactory.getMemoryMXBean();
        var heapMemoryUsage = memoryBean.getHeapMemoryUsage();
        
        return new SystemInfoVO.MemoryInfo(
                heapMemoryUsage.getCommitted(),
                heapMemoryUsage.getMax() - heapMemoryUsage.getUsed(),
                heapMemoryUsage.getUsed(),
                heapMemoryUsage.getMax() - heapMemoryUsage.getUsed(),
                100.0 * heapMemoryUsage.getUsed() / heapMemoryUsage.getMax()
        );
    }
    
    private List<SystemInfoVO.DiskInfo> getDiskInfoList() {
        File[] roots = File.listRoots();
        List<SystemInfoVO.DiskInfo> list = new ArrayList<>();
        for (File file : roots) {
            String path = file.getPath();
            SystemInfoVO.DiskInfo diskInfo = new SystemInfoVO.DiskInfo(
                    file.getPath(),
                    "0",
                    getDiskSpace(file),
                    "0",
                    getFreeDiskSpace(file),
                    "0"
            );
            list.add(diskInfo);
        }
        
        return list;
    }
    
    // 获取磁盘总空间（单位：GB）
    public static String getDiskSpace(File file) {
        long totalSpace = file.getTotalSpace();
        double totalSpaceGB = totalSpace / (1024.0 * 1024.0 * 1024.0);
        return String.format("%.2f", totalSpaceGB);
    }
    
    // 获取磁盘剩余空间（单位：GB）
    public static String getFreeDiskSpace(File file) {
        long freeSpace = file.getFreeSpace();
        double freeSpaceGB = freeSpace / (1024.0 * 1024.0 * 1024.0);
        return String.format("%.2f", freeSpaceGB);
    }
    
    private SystemInfoVO.JvmInfo getJvmInfo() {
        var runtimeMXBean = ManagementFactory.getRuntimeMXBean();
        var memoryBean = ManagementFactory.getMemoryMXBean();
        var heapMemoryUsage = memoryBean.getHeapMemoryUsage();
        
        Map<String, String> inputArguments = runtimeMXBean.getInputArguments().stream()
                .filter(arg -> arg.startsWith("-D"))
                .map(arg -> arg.substring(2).split("=", 2))
                .collect(Collectors.toMap(
                        parts -> parts[0],
                        parts -> parts.length > 1 ? parts[1] : ""
                ));
        
        Map<String, String> systemProperties = System.getProperties().stringPropertyNames().stream()
                .collect(Collectors.toMap(
                        name -> name,
                        name -> System.getProperty(name)
                ));
        
        return new SystemInfoVO.JvmInfo(
                System.getProperty("java.vm.name"),
                System.getProperty("java.vm.version"),
                System.getProperty("java.vm.vendor"),
                System.getProperty("java.home"),
                heapMemoryUsage.getCommitted(),
                heapMemoryUsage.getUsed(),
                heapMemoryUsage.getUsed(),
                heapMemoryUsage.getMax(),
                100.0 * heapMemoryUsage.getUsed() / heapMemoryUsage.getMax(),
                runtimeMXBean.getUptime(),
                LocalDateTime.ofInstant(Instant.ofEpochMilli(runtimeMXBean.getStartTime()), ZoneId.systemDefault()).toString(),
                inputArguments,
                systemProperties
        );
    }
    
    private SystemInfoVO.NetworkInfo getNetworkInfo() {
        try {
            String hostName = InetAddress.getLocalHost().getHostName();
            String ipAddress = null;
            String macAddress = null;
            
            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
            while (networkInterfaces.hasMoreElements()) {
                var networkInterface = networkInterfaces.nextElement();
                if (!networkInterface.isUp() || networkInterface.isVirtual() || networkInterface.isLoopback()) {
                    continue;
                }
                
                byte[] mac = networkInterface.getHardwareAddress();
                if (mac != null) {
                    StringBuilder macBuilder = new StringBuilder();
                    for (byte b : mac) {
                        macBuilder.append(String.format("%02X:", b));
                    }
                    if (macBuilder.length() > 0) {
                        macBuilder.deleteCharAt(macBuilder.length() - 1);
                    }
                    macAddress = macBuilder.toString();
                }
                
                Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
                while (inetAddresses.hasMoreElements()) {
                    var inetAddress = inetAddresses.nextElement();
                    if (!inetAddress.isLoopbackAddress() && inetAddress.getHostAddress().contains(".")) {
                        ipAddress = inetAddress.getHostAddress();
                        break;
                    }
                }
            }
            
            return new SystemInfoVO.NetworkInfo(hostName, ipAddress, macAddress);
        } catch (SocketException e) {
            throw new RuntimeException("Failed to get network information", e);
        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        }
    }
}